/*
 * button.c
 *
 *  Created on: 14 May 2015
 *      Author: yiqi.pan
 */
#include "common.h"
#include "config.h"
#include "button.h"
#include "timer_ms.h"

enum
{
	PRESS_INIT = 0,
	PRESS_UP,
	PRESS_DOWN,
	PRESS_DOWN_200MS,
	PRESS_DOWN_1S
};

typedef struct _BUTTON_CONTROL_t
{
	bool monitoring;
	uint8_t press_state;
	uint8_t last_press_event;
	timer_id_t timer_id;
	GPIO_TypeDef* gpio_port;
	uint8_t gpio_pin;
} BUTTON_CONTROL_t;

BUTTON_CONTROL_t btn_ctrl[NUMBER_OF_BUTTONS] =
{
		{
			{false},
			{PRESS_INIT},
			{NO_PRESS},
			{BUTTON_TIMER1},
			{USER_BUTTON_PORT},
			{USER_BUTTON_PIN}
		},
		{
			{false},
			{PRESS_INIT},
			{NO_PRESS},
			{BUTTON_TIMER2},
			{RESET_BUTTON_PORT},
			{RESET_BUTTON_PIN}
		}
};


#define IS_VALID_BUTTON(btn) (btn>=0 && btn<NUMBER_OF_BUTTONS)
#define btn_pin_state(btn_id) GPIO_ReadInputDataBit(btn_ctrl[btn_id].gpio_port, btn_ctrl[btn_id].gpio_pin)
#define btn_pin_state_is_high(btn_id) (btn_pin_state(btn_id))
#define btn_pin_state_is_low(btn_id) (!btn_pin_state(btn_id))

void init_btn(uint8_t btn_id)
{
    GPIO_InitTypeDef GPIO_InitStructure;  
    assert_param(IS_VALID_BUTTON(btn_id));

    /* Configure GPIO pin ----------------------------*/
    GPIO_InitStructure.GPIO_Pin =  btn_ctrl[btn_id].gpio_pin;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    //GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(btn_ctrl[btn_id].gpio_port, &GPIO_InitStructure);
}

void start_btn_monitor(uint8_t btn_id)
{
	assert_param(IS_VALID_BUTTON(btn_id));

	btn_ctrl[btn_id].monitoring = true;
	btn_ctrl[btn_id].press_state = PRESS_INIT;
	btn_ctrl[btn_id].last_press_event = NO_PRESS;


}

void stop_btn_monitor(uint8_t btn_id)
{
	assert_param(IS_VALID_BUTTON(btn_id));

	btn_ctrl[btn_id].monitoring = false;
	btn_ctrl[btn_id].last_press_event = NO_PRESS;


}

void btn_monitor(uint8_t btn_id)
{
	
	assert_param(IS_VALID_BUTTON(btn_id));

	if(!btn_ctrl[btn_id].monitoring)
		return;

	switch(btn_ctrl[btn_id].press_state)
	{
		case PRESS_INIT:
			// initialise power button monitoring with button unpressed
			if(btn_pin_state_is_high(btn_id))			
				btn_ctrl[btn_id].press_state = PRESS_UP;
			break;
		case PRESS_UP:
			if(btn_pin_state_is_low(btn_id)) // button is pressed
			{
				timer_ms_start_singleshot(btn_ctrl[btn_id].timer_id, 200);
				btn_ctrl[btn_id].press_state = PRESS_DOWN;
			}
			break;
		case PRESS_DOWN:
			if(btn_pin_state_is_high(btn_id)) // button is released
			{
				// button is released in 200ms, ignore it
				timer_ms_disactivate(btn_ctrl[btn_id].timer_id);
				btn_ctrl[btn_id].press_state = PRESS_UP;
			}
			else
			{
				// button is being pressed for over 200ms timeout
				if(timer_ms_test_and_clear_expired(btn_ctrl[btn_id].timer_id))
				{
					timer_ms_start_singleshot(btn_ctrl[btn_id].timer_id, 800); // continue to 1s timer
					btn_ctrl[btn_id].press_state = PRESS_DOWN_200MS;
				}
				else
				{
					// continue counting pressing time
				}
			}
			break;
		case PRESS_DOWN_200MS:
			if(btn_pin_state_is_high(btn_id)) // button is released between 200ms and 1s
			{
				btn_ctrl[btn_id].last_press_event = PRESS_200MS;
				timer_ms_disactivate(btn_ctrl[btn_id].timer_id);
				btn_ctrl[btn_id].press_state = PRESS_UP;
			}
			else
			{
				if(timer_ms_test_and_clear_expired(btn_ctrl[btn_id].timer_id))
				{
					// button is being pressed for over 1s
					timer_ms_start_singleshot(btn_ctrl[btn_id].timer_id, 3000); // continue to 4s timer
					btn_ctrl[btn_id].press_state = PRESS_DOWN_1S;
				}
				else
				{
					// continue counting pressing time
				}
			}
			break;
		case PRESS_DOWN_1S:
			if(btn_pin_state_is_high(btn_id)) // button is released between 1s and 4s
			{
				btn_ctrl[btn_id].last_press_event = PRESS_1S;
				timer_ms_disactivate(btn_ctrl[btn_id].timer_id);
				btn_ctrl[btn_id].press_state = PRESS_UP;
			}
			else
			{
				if(timer_ms_test_and_clear_expired(btn_ctrl[btn_id].timer_id))
				{
					// button is being pressed for over 4s
					btn_ctrl[btn_id].last_press_event = PRESS_4S;
					timer_ms_disactivate(btn_ctrl[btn_id].timer_id);
					btn_ctrl[btn_id].press_state = PRESS_INIT;
				}
				else
				{
					// continue counting pressing time
				}
			}
			break;
	}

}

uint8_t check_fetch_btn_event(uint8_t btn_id)
{
	uint8_t ret;
	btn_monitor(btn_id);

	ret = btn_ctrl[btn_id].last_press_event;
	if(ret != NO_PRESS)
	{
		btn_ctrl[btn_id].last_press_event = NO_PRESS;
	}
	return ret;
}
